﻿using IOP.SgrA;
using IOP.SgrA.SilkNet.Vulkan;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace VkSample910
{
    public class WindowConfig : IWindowConfig<Window, VulkanGraphicsManager>
    {
        public Task Config(Window window, VulkanGraphicsManager graphics)
        {
            var windowOption = window.Option;
            var lDevice = graphics.VulkanDevice;
            var swapchain = lDevice.CreateDefaultSwapChain(window).CreateDefaultSwapChainImageViews();
            VulkanImageView dImage = lDevice.CreateDepthBuffer((device, option) =>
            {
                var props = device.GetFormatProperties(Format.D16Unorm);
                option.Format = Format.D16Unorm;
                option.ImageType = ImageType.ImageType2D;
                option.Extent = new Extent3D(windowOption.Width, windowOption.Height, 1);
                option.MipLevels = 1;
                option.ArrayLayers = 1;
                option.Samples = SampleCountFlags.SampleCount1Bit;
                option.InitialLayout = ImageLayout.Undefined;
                option.SharingMode = SharingMode.Exclusive;
                if (props.LinearTilingFeatures.HasFlag(FormatFeatureFlags.FormatFeatureDepthStencilAttachmentBit))
                {
                    option.Tiling = ImageTiling.Linear;
                    return true;
                }
                else if (props.OptimalTilingFeatures.HasFlag(FormatFeatureFlags.FormatFeatureDepthStencilAttachmentBit))
                {
                    option.Tiling = ImageTiling.Optimal;
                    return true;
                }
                else return false;
            }, (option) =>
            {
                option.Format = Format.D16Unorm;
                option.Components = new ComponentMapping(ComponentSwizzle.R, ComponentSwizzle.G, ComponentSwizzle.B, ComponentSwizzle.A);
                option.SubresourceRange = new ImageSubresourceRange(ImageAspectFlags.ImageAspectDepthBit, 0, 1, 0, 1);
                option.ViewType = ImageViewType.ImageViewType2D;
            });
            var pass = lDevice.CreateRenderPass("Present",
                (device) =>
                {
                    AttachmentDescription[] attachments = new AttachmentDescription[2];
                    attachments[0] = new AttachmentDescription
                    {
                        Format = swapchain.SwapChainFormat.Format,
                        Samples = SampleCountFlags.SampleCount1Bit,
                        LoadOp = AttachmentLoadOp.Clear,
                        StoreOp = AttachmentStoreOp.Store,
                        StencilLoadOp = AttachmentLoadOp.DontCare,
                        StencilStoreOp = AttachmentStoreOp.DontCare,
                        InitialLayout = ImageLayout.Undefined,
                        FinalLayout = ImageLayout.PresentSrcKhr
                    };
                    attachments[1] = new AttachmentDescription
                    {
                        Format = Format.D16Unorm,
                        Samples = SampleCountFlags.SampleCount1Bit,
                        LoadOp = AttachmentLoadOp.Clear,
                        StoreOp = AttachmentStoreOp.DontCare,
                        StencilLoadOp = AttachmentLoadOp.DontCare,
                        StencilStoreOp = AttachmentStoreOp.DontCare,
                        InitialLayout = ImageLayout.Undefined,
                        FinalLayout = ImageLayout.DepthStencilAttachmentOptimal
                    };
                    return attachments;
                },
                (device) =>
                {
                    SubpassDescriptionOption[] subpasses = new SubpassDescriptionOption[]
                    {
                        new SubpassDescriptionOption
                        {
                            ColorAttachments = new AttachmentReference[]
                            {
                                new AttachmentReference { Attachment = 0, Layout = ImageLayout.ColorAttachmentOptimal }
                            },
                            DepthStencilAttachment = new AttachmentReference{ Attachment = 1,Layout = ImageLayout.DepthStencilAttachmentOptimal },
                            PipelineBindPoint = PipelineBindPoint.Graphics
                        }
                    };
                    return subpasses;
                },
                (device) =>
                {
                    return new SubpassDependency[0];
                }, RenderPassMode.Swapchain);
            pass.ConfigBeginInfo((option) =>
            {
                option.RenderArea = new Rect2D { Extent = new Extent2D(windowOption.Width, windowOption.Height), Offset = new Offset2D { X = 0, Y = 0 } };
                option.ClearValues = new ClearValue[]
                {
                        new ClearValue() { Color = new ClearColorValue(0.2f, 0.2f, 0.2f, 0.2f) },
                        new ClearValue() { DepthStencil = new ClearDepthStencilValue(1.0f, 0) }
                };
            });
            pass.CreateSwapchainFrameBuffer(swapchain, new VulkanImageView[] { dImage }, windowOption.Height, windowOption.Width);
            var i = lDevice.CreateSemaphore(ArrangeConstant.ImageAvailableSemaphore);
            var f = lDevice.CreateSemaphore(ArrangeConstant.RenderFinishSemaphore);
            window.CreateVulkanRenderDispatcher(lDevice.WorkQueues[0], swapchain, pass, i, f);
            window.ConfigResizeCallbackHandle(RasizeHandle);
            return Task.CompletedTask;
        }

        public void Initialization()
        {
        }

        private void RasizeHandle(VulkanGlfwWindow window, uint width, uint height)
        {
            window.RenderDispatcher.RecreateRenderDispatcher(width, height);
            var manager = window.GraphicsManager;
            float aspect = (float)width / height;
            foreach (var item in manager.GetCameras())
            {
                item.SetViewport(0, 0, width, height, 0, 1);
                item.SetProjectMatrix(-aspect, 1.0f, item.Near, item.Far, item.ProjectMode);
            }
        }
    }
}
